﻿using Gentings.Data;
using Gentings.Data.Internal;
using Gentings.Extensions;
using Microsoft.Extensions.Caching.Memory;
using System.Collections.Concurrent;

namespace Gentings.Saas.Sites
{
    /// <summary>
    /// 网站管理基类。
    /// </summary>
    /// <typeparam name="TSite">网站实例。</typeparam>
    public class SiteManager<TSite> : ISiteManager<TSite> where TSite : Site, new()
    {
        private readonly IEnumerable<ISiteEventHandler<TSite>> _eventHandlers;
        private readonly ConcurrentDictionary<string, IAreaProvider> _providers;
        private readonly IDbContext<UserSite> _context;

        /// <summary>
        /// 数据库操作接口。
        /// </summary>
        protected IDbContext<SiteAdapter> Context { get; }

        /// <summary>
        /// 缓存实例。
        /// </summary>
        protected IMemoryCache Cache { get; }

        /// <summary>
        /// 初始化类<see cref="SiteManager{TSite}"/>。
        /// </summary>
        /// <param name="context">数据库操作接口。</param>
        /// <param name="cache">缓存实例。</param>
        /// <param name="eventHandlers">网站事件处理器。</param>
        public SiteManager(IDbContext<SiteAdapter> context, IMemoryCache cache, IEnumerable<ISiteEventHandler<TSite>> eventHandlers, IEnumerable<IAreaProvider> providers)
        {
            _eventHandlers = eventHandlers.OrderByDescending(x => x.Priority);
            Context = context;
            Cache = cache;
            _context = context.As<UserSite>();
            _providers = new ConcurrentDictionary<string, IAreaProvider>(providers.Distinct(new AreaProviderComparer()).ToDictionary(x => x.Area), StringComparer.OrdinalIgnoreCase);
        }

        /// <summary>
        /// 如果结果正确返回<paramref name="succeed"/>，否则返回失败项。
        /// </summary>
        /// <param name="siteId">网站id。</param>
        /// <param name="result">执行结果。</param>
        /// <param name="succeed">执行成功返回的值。</param>
        /// <returns>返回执行结果实例对象。</returns>
        protected DataResult FromResult(int siteId, bool result, DataAction succeed)
        {
            if (result)
            {
                Cache.Remove(GetCacheKey(siteId));
                Cache.Remove(typeof(SiteDomain));//刷新网站域名
                return succeed;
            }
            return (DataAction)(-(int)succeed);
        }

        /// <summary>
        /// 刷新缓存。
        /// </summary>
        /// <param name="result">执行结果。</param>
        /// <param name="ids">网站ID列表。</param>
        /// <returns>返回执行结果。</returns>
        protected bool Refresh(bool result, int[] ids)
        {
            if (result)
            {
                foreach (var id in ids)
                {
                    Cache.Remove(GetCacheKey(id));
                }
            }

            return result;
        }

        /// <summary>
        /// 获取缓存键。
        /// </summary>
        /// <param name="siteId">网站id。</param>
        /// <returns>返回缓存键实例。</returns>
        protected string GetCacheKey(int siteId) => $"Site{siteId}";

        /// <summary>
        /// 获取网站实例。
        /// </summary>
        /// <param name="id">网站Id。</param>
        /// <returns>返回网站实例。</returns>
        public virtual TSite? Find(int id)
        {
            return Cache.GetOrCreate(GetCacheKey(id), ctx =>
            {
                ctx.SetDefaultAbsoluteExpiration();
                return Context.Find(id)?.AsSite<TSite>();
            });
        }

        /// <summary>
        /// 获取网站实例。
        /// </summary>
        /// <param name="id">网站Id。</param>
        /// <returns>返回网站实例。</returns>
        public virtual Task<TSite?> FindAsync(int id)
        {
            return Cache.GetOrCreateAsync(GetCacheKey(id), async ctx =>
             {
                 ctx.SetDefaultAbsoluteExpiration();
                 var site = await Context.FindAsync(id);
                 return site?.AsSite<TSite>();
             });
        }

        /// <summary>
        /// 判断唯一键是否已经存在。
        /// </summary>
        /// <param name="site">网站实例对象。</param>
        /// <returns>返回判断结果。</returns>
        public virtual bool IsDuplicated(TSite site) => Context.Any(x => x.SiteKey == site.SiteKey && x.Id != site.Id);

        /// <summary>
        /// 判断唯一键是否已经存在。
        /// </summary>
        /// <param name="site">网站实例对象。</param>
        /// <returns>返回判断结果。</returns>
        public virtual Task<bool> IsDuplicatedAsync(TSite site) => Context.AnyAsync(x => x.SiteKey == site.SiteKey && x.Id != site.Id);

        /// <summary>
        /// 保存当前实例。
        /// </summary>
        /// <param name="site">网站实例对象。</param>
        /// <returns>返回保存结果。</returns>
        public virtual DataResult Save(TSite site)
        {
            var adapter = SiteAdapter.FromSite(site);
            if (adapter.Id > 0)
            {
                return FromResult(adapter.Id, Context.BeginTransaction(db =>
                {
                    if (!db.Update(adapter))
                        return false;
                    return SaveDomains(db, adapter.Id, site.ToDomainList());
                }), DataAction.Updated);
            }
            return FromResult(adapter.Id, Context.BeginTransaction(db =>
            {
                if (!db.Create(adapter))
                    return false;
                site.Id = adapter.Id;
                if (!SaveDomains(db, site.Id, site.ToDomainList()))
                    return false;
                if (!db.As<UserSite>().Create(new UserSite { SiteId = site.Id, UserId = site.UserId }))
                    return false;
                foreach (var eventHandler in _eventHandlers)
                {
                    if (!eventHandler.OnCreated(db, site))
                    {
                        site.Id = 0;
                        return false;
                    }
                }
                return true;
            }, 600), DataAction.Created);
        }

        private bool SaveDomains(IDbTransactionContext<SiteAdapter> context, int id, List<string> domains)
        {
            var db = context.As<SiteDomain>();
            db.Delete(x => x.SiteId == id);
            foreach (var domain in domains)
            {
                if (!db.Create(new SiteDomain { Domain = domain, SiteId = id }))
                    return false;
            }
            return true;
        }

        /// <summary>
        /// 保存当前实例。
        /// </summary>
        /// <param name="site">网站实例对象。</param>
        /// <returns>返回保存结果。</returns>
        public virtual async Task<DataResult> SaveAsync(TSite site)
        {
            var adapter = SiteAdapter.FromSite(site);
            if (adapter.Id > 0)
            {
                return FromResult(adapter.Id, await Context.BeginTransactionAsync(async db =>
                {
                    if (!await db.UpdateAsync(adapter))
                        return false;
                    return await SaveDomainsAsync(db, site.Id, site.ToDomainList());
                }), DataAction.Updated);
            }
            return FromResult(adapter.Id, await Context.BeginTransactionAsync(async db =>
            {
                if (!await db.CreateAsync(adapter))
                    return false;
                site.Id = adapter.Id;
                if (!await SaveDomainsAsync(db, site.Id, site.ToDomainList()))
                    return false;
                if (!await db.As<UserSite>().CreateAsync(new UserSite { SiteId = site.Id, UserId = site.UserId }))
                    return false;
                foreach (var eventHandler in _eventHandlers)
                {
                    if (!await eventHandler.OnCreatedAsync(db, site))
                    {
                        site.Id = 0;
                        return false;
                    }
                }
                return true;
            }, 600), DataAction.Created);
        }

        private async Task<bool> SaveDomainsAsync(IDbTransactionContext<SiteAdapter> context, int id, List<string> domains)
        {
            var db = context.As<SiteDomain>();
            await db.DeleteAsync(x => x.SiteId == id);
            foreach (var domain in domains)
            {
                if (!await db.CreateAsync(new SiteDomain { Domain = domain, SiteId = id }))
                    return false;
            }
            return true;
        }

        /// <summary>
        /// 删除当前实例。
        /// </summary>
        /// <param name="ids">网站Id列表。</param>
        /// <returns>返回删除结果。</returns>
        public virtual DataResult Delete(int[] ids)
        {
            var result = Refresh(Context.BeginTransaction(db =>
            {
                foreach (var eventHandler in _eventHandlers)
                {
                    if (!eventHandler.OnDelete(db, ids))
                        return false;
                }

                return db.Delete(x => x.Id.Included(ids));
            }, 600), ids);
            return DataResult.FromResult(result, DataAction.Created);
        }

        /// <summary>
        /// 删除当前实例。
        /// </summary>
        /// <param name="ids">网站Id列表。</param>
        /// <returns>返回删除结果。</returns>
        public virtual async Task<DataResult> DeleteAsync(int[] ids)
        {
            var result = Refresh(await Context.BeginTransactionAsync(async db =>
            {
                foreach (var eventHandler in _eventHandlers)
                {
                    if (!await eventHandler.OnDeleteAsync(db, ids))
                        return false;
                }

                return await db.DeleteAsync(x => x.Id.Included(ids));
            }, 600), ids);
            return DataResult.FromResult(result, DataAction.Created);
        }

        /// <summary>
        /// 获取激活的网站列表。
        /// </summary>
        /// <returns>返回所有激活的网站列表。</returns>
        public virtual IEnumerable<Site> Load()
        {
            return Context.As<Site>()
                .AsQueryable()
                .WithNolock()
                .Where(x => !x.Disabled)
                .AsEnumerable();
        }

        /// <summary>
        /// 获取激活的网站列表。
        /// </summary>
        /// <returns>返回所有激活的网站列表。</returns>
        public virtual Task<IEnumerable<Site>> LoadAsync()
        {
            return Context.As<Site>()
                .AsQueryable()
                .WithNolock()
                .Where(x => !x.Disabled)
                .AsEnumerableAsync();
        }

        /// <summary>
        /// 分页查询网站实例。
        /// </summary>
        /// <param name="query">网站查询实例。</param>
        /// <returns>返回当前网站列表。</returns>
        public virtual IPageEnumerable<Site> Load(SiteQuery query)
        {
            return Context.Load<SiteQuery, Site>(query);
        }

        /// <summary>
        /// 分页查询网站实例。
        /// </summary>
        /// <param name="query">网站查询实例。</param>
        /// <returns>返回当前网站列表。</returns>
        public virtual Task<IPageEnumerable<Site>> LoadAsync(SiteQuery query)
        {
            return Context.LoadAsync<SiteQuery, Site>(query);
        }

        /// <summary>
        /// 启用网站。
        /// </summary>
        /// <param name="ids">启用Id列表。</param>
        /// <returns>返回启用结果。</returns>
        public virtual bool Enabled(int[] ids)
        {
            return Refresh(Context.Update(x => x.Id.Included(ids), new { Disabled = false }), ids);
        }

        /// <summary>
        /// 启用网站。
        /// </summary>
        /// <param name="ids">启用Id列表。</param>
        /// <returns>返回启用结果。</returns>
        public virtual async Task<bool> EnabledAsync(int[] ids)
        {
            return Refresh(await Context.UpdateAsync(x => x.Id.Included(ids), new { Disabled = false }), ids);
        }

        /// <summary>
        /// 禁用网站。
        /// </summary>
        /// <param name="ids">禁用Id列表。</param>
        /// <returns>返回禁用结果。</returns>
        public virtual bool Disabled(int[] ids)
        {
            return Refresh(Context.Update(x => x.Id.Included(ids), new { Disabled = true }), ids);
        }

        /// <summary>
        /// 禁用网站。
        /// </summary>
        /// <param name="ids">禁用Id列表。</param>
        /// <returns>返回禁用结果。</returns>
        public virtual async Task<bool> DisabledAsync(int[] ids)
        {
            return Refresh(await Context.UpdateAsync(x => x.Id.Included(ids), new { Disabled = true }), ids);
        }

        /// <summary>
        /// 是否已经有网站实例。
        /// </summary>
        /// <returns>返回判断结果。</returns>
        public virtual bool Any()
        {
            return Context.Any();
        }

        /// <summary>
        /// 是否已经有网站实例。
        /// </summary>
        /// <returns>返回判断结果。</returns>
        public virtual Task<bool> AnyAsync()
        {
            return Context.AnyAsync();
        }

        /// <summary>
        /// 获取网站对应的区域。
        /// </summary>
        /// <param name="areaName">区域名称。</param>
        /// <returns>返回网站对于区域提供者。</returns>
        public virtual IAreaProvider? GetProvider(string areaName)
        {
            _providers.TryGetValue(areaName, out var provider);
            return provider;
        }

        /// <summary>
        /// 获取网站对应的区域列表。
        /// </summary>
        /// <returns>返回网站对于区域提供者列表。</returns>
        public virtual IEnumerable<IAreaProvider> GetProviders() => _providers.Values;

        /// <summary>
        /// 获取当前网站Id的用户Id列表。
        /// </summary>
        /// <param name="siteId">网站Id。</param>
        /// <returns>返回用户Id列表。</returns>
        public virtual IEnumerable<int> GetUsers(int siteId)
        {
            return _context.AsQueryable().Select(x => x.UserId).Where(x => x.SiteId == siteId).AsEnumerable(x => x.GetInt32(0));
        }

        /// <summary>
        /// 获取当前网站Id的用户Id列表。
        /// </summary>
        /// <param name="siteId">网站Id。</param>
        /// <returns>返回用户Id列表。</returns>
        public virtual Task<IEnumerable<int>> GetUsersAsync(int siteId)
        {
            return _context.AsQueryable().Select(x => x.UserId).Where(x => x.SiteId == siteId).AsEnumerableAsync(x => x.GetInt32(0));
        }

        /// <summary>
        /// 设置用户列表。
        /// </summary>
        /// <param name="siteId">网站Id。</param>
        /// <param name="userIds">用户Id列表。</param>
        /// <returns>返回设置结果。</returns>
        public virtual bool SetUsers(int siteId, int[] userIds)
        {
            return _context.BeginTransaction(db =>
            {
                db.Delete(x => x.SiteId == siteId);
                foreach (var userId in userIds)
                {
                    if (!db.Create(new UserSite { SiteId = siteId, UserId = userId }))
                        return false;
                }
                return true;
            });
        }

        /// <summary>
        /// 设置用户列表。
        /// </summary>
        /// <param name="siteId">网站Id。</param>
        /// <param name="userIds">用户Id列表。</param>
        /// <returns>返回设置结果。</returns>
        public virtual Task<bool> SetUsersAsync(int siteId, int[] userIds)
        {
            return _context.BeginTransactionAsync(async db =>
            {
                await db.DeleteAsync(x => x.SiteId == siteId);
                foreach (var userId in userIds)
                {
                    if (!await db.CreateAsync(new UserSite { SiteId = siteId, UserId = userId }))
                        return false;
                }
                return true;
            });
        }

        /// <summary>
        /// 是否网站管理用户。
        /// </summary>
        /// <param name="siteId">网站Id。</param>
        /// <param name="userId">用户Id。</param>
        /// <returns>返回判断结果。</returns>
        public virtual bool IsSiteUser(int siteId, int userId)
        {
            return _context.Any(x => x.SiteId == siteId && x.UserId == userId);
        }

        /// <summary>
        /// 是否网站管理用户。
        /// </summary>
        /// <param name="siteId">网站Id。</param>
        /// <param name="userId">用户Id。</param>
        /// <returns>返回判断结果。</returns>
        public virtual Task<bool> IsSiteUserAsync(int siteId, int userId)
        {
            return _context.AnyAsync(x => x.SiteId == siteId && x.UserId == userId);
        }
    }
}